import assert from "assert";
import { inherits as inherits$0 } from "util";
import * as utils from "./utils";
import Cipher from "./cipher";
'use strict';
var inherits = { inherits: inherits$0 }.inherits;
function DESState() {
    this.tmp = new Array(2);
    this.keys = null;
}
function DES(options) {
    Cipher.call(this, options);
    var state = new DESState();
    this._desState = state;
    this.deriveKeys(state, options.key);
}
inherits(DES, Cipher);
DES.create = function create(options) {
    return new DES(options);
};
var shiftTable = [
    1, 1, 2, 2, 2, 2, 2, 2,
    1, 2, 2, 2, 2, 2, 2, 1
];
DES.prototype.deriveKeys = function deriveKeys(state, key) {
    state.keys = new Array(16 * 2);
    assert.equal(key.length, this.blockSize, 'Invalid key length');
    var kL = utils.readUInt32BE(key, 0);
    var kR = utils.readUInt32BE(key, 4);
    utils.pc1(kL, kR, state.tmp, 0);
    kL = state.tmp[0];
    kR = state.tmp[1];
    for (var i = 0; i < state.keys.length; i += 2) {
        var shift = shiftTable[i >>> 1];
        kL = utils.r28shl(kL, shift);
        kR = utils.r28shl(kR, shift);
        utils.pc2(kL, kR, state.keys, i);
    }
};
DES.prototype._update = function _update(inp, inOff, out, outOff) {
    var state = this._desState;
    var l = utils.readUInt32BE(inp, inOff);
    var r = utils.readUInt32BE(inp, inOff + 4);
    // Initial Permutation
    utils.ip(l, r, state.tmp, 0);
    l = state.tmp[0];
    r = state.tmp[1];
    if (this.type === 'encrypt')
        this._encrypt(state, l, r, state.tmp, 0);
    else
        this._decrypt(state, l, r, state.tmp, 0);
    l = state.tmp[0];
    r = state.tmp[1];
    utils.writeUInt32BE(out, l, outOff);
    utils.writeUInt32BE(out, r, outOff + 4);
};
DES.prototype._pad = function _pad(buffer, off) {
    var value = buffer.length - off;
    for (var i = off; i < buffer.length; i++)
        buffer[i] = value;
    return true;
};
DES.prototype._unpad = function _unpad(buffer) {
    var pad = buffer[buffer.length - 1];
    for (var i = buffer.length - pad; i < buffer.length; i++)
        assert.equal(buffer[i], pad);
    return buffer.slice(0, buffer.length - pad);
};
DES.prototype._encrypt = function _encrypt(state, lStart, rStart, out, off) {
    var l = lStart;
    var r = rStart;
    // Apply f() x16 times
    for (var i = 0; i < state.keys.length; i += 2) {
        var keyL = state.keys[i];
        var keyR = state.keys[i + 1];
        // f(r, k)
        utils.expand(r, state.tmp, 0);
        keyL ^= state.tmp[0];
        keyR ^= state.tmp[1];
        var s = utils.substitute(keyL, keyR);
        var f = utils.permute(s);
        var t = r;
        r = (l ^ f) >>> 0;
        l = t;
    }
    // Reverse Initial Permutation
    utils.rip(r, l, out, off);
};
DES.prototype._decrypt = function _decrypt(state, lStart, rStart, out, off) {
    var l = rStart;
    var r = lStart;
    // Apply f() x16 times
    for (var i = state.keys.length - 2; i >= 0; i -= 2) {
        var keyL = state.keys[i];
        var keyR = state.keys[i + 1];
        // f(r, k)
        utils.expand(l, state.tmp, 0);
        keyL ^= state.tmp[0];
        keyR ^= state.tmp[1];
        var s = utils.substitute(keyL, keyR);
        var f = utils.permute(s);
        var t = l;
        l = (r ^ f) >>> 0;
        r = t;
    }
    // Reverse Initial Permutation
    utils.rip(l, r, out, off);
};
export default DES;
